#include "image_texture_gl.h"

#include <IL/il.h>

#include "error_gl.h"
#include "image.h"

namespace venus {
ImageTextureGL::~ImageTextureGL(void) {
  this->Release();
}

void ImageTextureGL::Bind() const {
  if (0 == tex_id_) {
    Error("'ImageTextureGL::Bind', invalid texture");
    return;
  }
  glBindTexture(target_, tex_id_);
}

void ImageTextureGL::Unbind() const {
  if (0 == tex_id_) {
    Error("'ImageTextureGL::Unbind', invalid texture");
    return;
  }
  GLint current_id = 0;
  glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_id);
  if (tex_id_ != current_id) {
    Error("'ImageTextureGL::Unbind', texture not bound");
    return;
  }
  glBindTexture(target_, 0);
}

bool ImageTextureGL::Initialize(const Image &image, GLuint internal_format,
                                int opts /*= kUseDefaultParameters*/) {
  Info("ImageTextureGL::Initialize");
  if (!BeginInitialize()) return false;
  if (kUseDefaultParameters & opts) this->SetDefaultParameters();
  if (kUseMipmaps & opts) {
    gluBuild2DMipmaps(target_, internal_format, image.width(), image.height(),
                      image.format(), image.type(), image.data());
  } else {
    glTexImage2D(target_, 0, internal_format, image.width(), image.height(), 0, 
                 image.format(), image.type(), image.data());
  }
  this->width_ = image.width(); this->height_ = image.height();
  glBindTexture(target_, 0); 
  return !CheckErrorsGL("ImageTextureGL::BeginInitialize");
}

bool ImageTextureGL::Initialize(int width, int height, GLuint internal_format,
                                int opts /*= kUseDefaultParameters*/) {
  Info("ImageTextureGL::Initialize");
  if (!BeginInitialize()) return false;
  if (kUseDefaultParameters & opts) this->SetDefaultParameters();
  GLint type = GL_FLOAT;
  GLint format = GL_RGBA;
  if (kDepthTexture & opts) {
    format = GL_DEPTH_COMPONENT;
    if (GL_DEPTH_COMPONENT == internal_format)  type = GL_UNSIGNED_BYTE;
  } else if (GL_RGBA32UI == internal_format) {
    type = GL_UNSIGNED_INT;
    format = GL_RGBA_INTEGER;
  }
  if (kUseMipmaps & opts) {
    gluBuild2DMipmaps(target_, internal_format, width, height, 
                      format, type, NULL);
  } else {
    glTexImage2D(target_, 0, internal_format, width, height, 0, 
                 format, type, NULL);
  }
  this->width_ = width; this->height_ = height;
  glBindTexture(target_, 0); 
  return !CheckErrorsGL("ImageTextureGL::Initialize");
}


void ImageTextureGL::SetDefaultParameters() {
  glPixelStorei(GL_UNPACK_ALIGNMENT , 1); 
  glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
  glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
  glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
  glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
}

bool ImageTextureGL::BeginInitialize(void) {	
  Info("ImageTextureGL::BeginInitialize");
  if (0 != tex_id_) {
    Error("'ImageTextureGL::BeginInitialize', texture already initialized");
    return false;
  }
  if (CheckErrorsGL("ImageTextureGL::BeginInitialize")) return false;
  glGenTextures(1, &tex_id_);
  glBindTexture(target_, tex_id_);
  return !CheckErrorsGL("ImageTextureGL::BeginInitialize");
}

void ImageTextureGL::Save(const char *filename) const {
  this->Bind();
  int width, height;
  glGetTexLevelParameteriv(target_, 0, GL_TEXTURE_WIDTH, &width);
  glGetTexLevelParameteriv(target_, 0, GL_TEXTURE_HEIGHT, &height);
  float* data = new float[4 * width * height];
  glGetTexImage(target_, 0, GL_RGBA, GL_FLOAT, data);
  ilInit();
  ILuint id = ilGenImage();
  ilTexImage(width, height, 1, 4, IL_RGBA, IL_FLOAT, data);
  ilEnable(IL_FILE_OVERWRITE);
  ilSaveImage(filename);	
  ilDeleteImage(id);
  delete[] data;
}

void ImageTextureGL::Release(void) {
  if (tex_id_ > 0) {
    glDeleteTextures(1, &tex_id_);
    tex_id_ = 0;
    width_ = height_ = 0;
  }
}

} // namespace venus

